package com.mmall.service;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.mmall.beans.CacheKeyConstants;
import com.mmall.common.RequestHolder;
import com.mmall.dao.SysAclMapper;
import com.mmall.dao.SysAclModuleMapper;
import com.mmall.dao.SysRoleAclMapper;
import com.mmall.dao.SysRoleUserMapper;
import com.mmall.dto.RequestPremissionDto;
import com.mmall.model.SysAcl;
import com.mmall.model.SysAclModule;
import com.mmall.model.SysUser;
import com.mmall.util.IpUtil;
import com.mmall.util.JsonMapper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.jackson.type.TypeReference;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class SysCoreService {
    @Resource
    private SysAclMapper sysAclMapper;
    @Resource
    private SysAclService sysAclService;
    @Resource
    private SysRoleUserMapper sysRoleUserMapper;
    @Resource
    private SysRoleAclMapper sysRoleAclMapper;
    @Resource
    private SysCacheService sysCacheService;
    @Resource
    private SysAclModuleMapper sysAclModuleMapper;
    @Resource
    private SysAclModuleService sysAclModuleService;

    /**
     * 获取当前登录用户的权限点
     * @return
     */
    public List<SysAcl> getCurrentUserAclList() {
        //获取当前登录用户的id
        int userId = RequestHolder.getCurrentUser().getId();
        //根据id查询对应的权限点
        return getUserAclList(userId);
    }

    public List<SysAcl> getRoleAclList(int roleId) {
        List<Integer> aclIdList = sysRoleAclMapper.getAclIdListByRoleIdList(Lists.<Integer>newArrayList(roleId));
        if (CollectionUtils.isEmpty(aclIdList)){
            return Lists.newArrayList();
        }
        return sysAclMapper.getByIdList(aclIdList);
    }

    public List<SysAcl> getUserAclList(int userId) {
        if (isSuperAdmin()) {
            return sysAclMapper.getAll();
        }
        //先拿到对应的roleId
        List<Integer> userRoleIdList = sysRoleUserMapper.getRoleIdListByUserId(userId);
        if (CollectionUtils.isEmpty(userRoleIdList)){
            return Lists.newArrayList();
        }
        //通过roleId拿到权限模块id
        List<Integer> roleAclIdList = sysRoleAclMapper.getAclIdListByRoleIdList(userRoleIdList);
        if (CollectionUtils.isEmpty(roleAclIdList)){
            return Lists.newArrayList();
        }
        //通过权限模块id拿到权限
        return sysAclMapper.getByIdList(roleAclIdList);
    }

    /**
     * 是否为超级管理员
     * @return
     */
    public boolean isSuperAdmin() {
        // 这里是我自己定义了一个假的超级管理员规则，实际中要根据项目进行修改
        // 可以是配置文件获取，可以指定某个用户，也可以指定某个角色
        SysUser sysUser = RequestHolder.getCurrentUser();
        if (sysUser.getMail().contains("admin")) {
            return true;
        }
        return false;
    }

    /**
     * 一个用户是否有该访问权限
     * @param url
     * @return
     */
    public boolean hasUrlAcl(String url) {
        if (isSuperAdmin()){
            return true;
        }
        List<SysAcl> aclList = sysAclMapper.getByUrl(url);
        if (CollectionUtils.isEmpty(aclList)){
            return true;
        }
        List<SysAcl> userAcls = getCurrentUserAclListFromCache();//getUserAclList(RequestHolder.getCurrentUser().getId());//

        Set<Integer> aclIds = userAcls.stream().map(acl->acl.getId()).collect(Collectors.toSet());

        boolean hasValidAcl = false;//初始化权限的状态

        //规则：只要有一个权限点有权限，那么我们就认为有访问权限
        for (SysAcl acl : aclList){
           if (acl == null || acl.getStatus() != 1){
               //权限不存在，或者是无效的，继续进行下一个的校验
               continue;
           }
           hasValidAcl = true;//如何权限不存在，或者是无效的，那么还需要给他一个可以访问该url的状态
           if (aclIds.contains(acl.getId())){
               return true;
           }

        }

        if (!hasValidAcl){
            //当前所有权限是无效的，那么返回true，通过
            return true;
        }

        return false;
    }

    public List<SysAcl> getCurrentUserAclListFromCache(){
        int userId = RequestHolder.getCurrentUser().getId();
        String cacheValue = sysCacheService.getFromCache(CacheKeyConstants.USER_ACLS,String.valueOf(userId));
        if (StringUtils.isBlank(cacheValue)){
            List<SysAcl> acls = getCurrentUserAclList();
            if (CollectionUtils.isNotEmpty(acls)){
                sysCacheService.saveCache(JsonMapper.objToString(acls),600,CacheKeyConstants.USER_ACLS,String.valueOf(userId));
            }
            return acls;
        }
        return JsonMapper.stringToObj(cacheValue, new TypeReference<List<SysAcl>>() {
        });
    }

    /**
     * 初始化权限，将没有的权限自动加载到数据库
     * @param scanAclPackages
     */
    public void invalidAcl(List<RequestPremissionDto> scanAclPackages) {

        if (CollectionUtils.isEmpty(scanAclPackages)) {
           return;
        }
        saveAclModules(scanAclPackages);
    }

    /**
     * 插入权限模块
     * @param scanAclPackages
     */
    private void saveAclModules(List<RequestPremissionDto> scanAclPackages) {

        if (CollectionUtils.isEmpty(scanAclPackages))
            return;

        Map<String,SysAclModule> moduleMap = sysAclModuleMapper.getAllAclModule().stream().collect(Collectors.toMap(SysAclModule::getName,a->a,(k1,k2)->k1));
        Set<String> urlSets = sysAclMapper.getAll().stream().map(acl->acl.getUrl()).collect(Collectors.toSet());
        //应对数据中存在重复的
        Set<String> idents = Sets.newConcurrentHashSet();

        for (RequestPremissionDto dto : scanAclPackages){
            SysAclModule sysAclModule = SysAclModule.builder()
                    .name(dto.getDescription())
                    .level("0")
                    .parentId(0)
                    .seq(1)
                    .operateIp("127.0.0.1")
                    .operateTime(new Date())
                    .operator("系统自动生成")
                    .remark(dto.getDescription())
                    .build();

            if (moduleMap.get(sysAclModule.getName())==null){
                sysAclModuleMapper.insertSelective(sysAclModule);
            }else {
                sysAclModule.setId(moduleMap.get(sysAclModule.getName()).getId());
            }

            List<RequestPremissionDto> aclList = dto.getDtos();
            if (CollectionUtils.isEmpty(aclList))
                return;
            int moduleId = sysAclModule.getId();
            String classUrl = dto.getUrl();
            saveAcls(moduleId, classUrl, aclList,urlSets,idents);
        }
    }

    /**
     * 插入权限点
     * @param moduleId
     * @param classUrl
     * @param aclList
     * @return
     */
    private void saveAcls(int moduleId, String classUrl, List<RequestPremissionDto> aclList,Set<String> urlSets,Set<String> idents) {

        List<SysAcl> acls = Lists.newLinkedList();
        for (RequestPremissionDto methodDto : aclList){
            if (!urlSets.contains(methodDto.getUrl()) && !idents.contains(methodDto.getUrl())) {
                idents.add(methodDto.getUrl());
                SysAcl acl = SysAcl.builder()
                        .name(methodDto.getDescription())
                        .url(methodDto.getUrl())
                        .aclModuleId(moduleId)
                        .remark(methodDto.getDescription())
                        .type(methodDto.getUrl().endsWith(".page") ? 1 : 2)
                        .code(generateCode())
                        .operateIp("127.0.0.1")
                        .operateTime(new Date())
                        .operator("系统自动生成")
                        .status(1)
                        .seq(1)
                        .build();

                acls.add(acl);
            }
        }
        if (CollectionUtils.isNotEmpty(acls))
        sysAclMapper.batchSaveAcls(acls);

    }

    /**
     * 生成权限编码
     * @return
     */
    public String generateCode() {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        return dateFormat.format(new Date()) + "_" + (int)(Math.random() * 100);
    }
}
